package com.theten52.tutorial.generics.d1;

interface ThingInfo<T> {
    // just an example method showing that ThingInfo needs to know about
    // the type parameter T
    T getThing();
}

class Thing<T extends Thing<T>> {
    // I need to be able to return a ThingInfo with the type parameter
    // of the sub class of Thing. ie. ThingA.getThingInfo() must return
    // a ThingInfo<ThingA>.
    // This is where Java would benefit from self types, as I could declare
    // the method something like: ThingInfo<THIS_TYPE> getThingInfo()
    // and Thing would not need a type parameter.
    ThingInfo<T> getThingInfo() {
        return null;
    }
}

// example Thing implementation
class ThingA extends Thing<ThingA> {
}

// example Thing implementation
class ThingB extends Thing<ThingB> {
}

class ThingRelation<X extends Thing<X>, Y extends Thing<Y>> {
    X getParent() {
        return null;
    }

    Y getChild() {
        return null;
    }

    <Z extends Thing<Z>> void useRelation(ThingRelation<Y, Z> relation) {
        // do something;
    }
}

public class Thing1Demo {
    public static void main(String[] args) {
        ThingA thingA = new ThingA();
        ThingInfo<ThingA> thingInfo = thingA.getThingInfo();

        ThingB thingB = new ThingB();
        ThingInfo<ThingB> thingInfo1 = thingB.getThingInfo();

        ThingRelation<ThingA, ThingB> thingAThingBThingRelation = new ThingRelation<>();
        ThingB child = thingAThingBThingRelation.getChild();
        ThingA parent = thingAThingBThingRelation.getParent();

        ThingRelation<ThingB, ThingA> thingBThingAThingRelation = new ThingRelation<>();

        thingAThingBThingRelation.useRelation(thingBThingAThingRelation);
    }
}